#include <iostream>
#include <string>
#include <sstream>
#include <map>
#include <cctype>

#define DEBUG

enum Token_value {
  NAME,        NUMBER,       END,
  PLUS='+',    MINUS='-',    MUL='*',  DIV='/',
  PRINT=';',   ASSIGN='=',   LP='(',   RP=')'
};

Token_value curr_tok = PRINT;
int no_of_errors;
int line;
double number_value;
std::string string_value;
std::map<std::string, double> table;
std::istream* input;

double expr(bool get);
double term(bool get);
double prim(bool get);
double error(const std::string& s);
Token_value get_token();
void output_curr_tok();




void
output_curr_tok()
{
  switch (curr_tok)
    {
    case NAME:
      {
	std::cerr << "curr_tok = NAME" << std::endl;
	break;
      }
    case NUMBER:
      {
	std::cerr << "curr_tok = NUMBER" << std::endl;
	break;
      }
    case END:
      {
	std::cerr << "curr_tok = END" << std::endl;
	break;
      }
    default:
      {
	std::cerr << "curr_tok = " << char(curr_tok) << std::endl;
	break;
      }
    }
}


double
expr(bool get)
{
#ifdef DEBUG
  std::cerr << "in expr(). " << std::endl;
#endif

  double left = term(get);

#ifdef DEBUG
  std::cerr << "left = " << left << std::endl;
#endif

  while (1)
    {

#ifdef DEBUG
      std::cerr << "in expr(). ";
      output_curr_tok();
#endif

      switch (curr_tok)
	{
	case PLUS:
	  {
	    left += term(true);

#ifdef DEBUG
	    std::cerr << "left = " << left << std::endl;
#endif

	    break;
	  }
	case MINUS:
	  {
	    left -= term(true);

#ifdef DEBUG
	    std::cerr << "left = " << left << std::endl;
#endif

	    break;
	  }
	default:

#ifdef DEBUG
	  std::cerr << "RETURN left = " << left << std::endl;
#endif

	  return left;
	}
    }
}


double
term(bool get)
{
#ifdef DEBUG
  std::cerr << "in term()" << std::endl;
#endif

  double left = prim(get);
  while (1)
    {

#ifdef DEBUG
      std::cerr << "in term(). ";
      output_curr_tok();
#endif

      switch (curr_tok)
	{
	case MUL:
	  {
	    left *= prim(true);
	    break;
	  }
	case DIV:
	  {
	    if (double d = prim(true))
	      {
		left /= d;
		break;
	      }
	    return error("divide by 0");
	  }
	default:
	  return left;
	}
    }
}


double
prim(bool get)
{
#ifdef DEBUG
  std::cerr << "in prim()" << std::endl;
#endif

  if (get)
    {
      get_token();
    }

#ifdef DEBUG
  std::cerr << "in prim(). ";
  output_curr_tok();
#endif

  switch (curr_tok)
    {
    case NUMBER:
      {
	double v = number_value;
	get_token();
	
#ifdef DEBUG
	std::cerr << "return " << v << std::endl;
#endif

	return v;
      }
    case NAME:
      {

#ifdef DEBUG
	std::cerr << "string_value = " << string_value << std::endl;
#endif
	// here: map (string_value ~~> v) is automatically added to table.
	double& v = table[string_value];
	if (get_token() == ASSIGN)
	  {
	    v = expr(true);
	  }

#ifdef DEBUG
	std::cerr << "return " << v << std::endl;
#endif

	return v;
      }
    case MINUS:
      {
	return -prim(true);
      }
    case LP:
      {
	double e = expr(true);
	if (curr_tok != RP)
	  {
	    return error("')' expected");
	  }
	get_token(); // eat ')'
	return e;
      }
    default:
      return error("primary expected");
    }
}


double
error(const std::string& s)
{
  no_of_errors++;
  std::cerr << "ERROR at line " << line << ": " << s << std::endl;
  return 1;
}


Token_value
get_token()
{
#ifdef DEBUG
  std::cerr << "in get_token()" << std::endl;
#endif

  char ch;
  do // skip whitespace except '\n'
    {
      if (!input->get(ch))
	{
	  return curr_tok = END;
	}
    }
  while (ch != '\n' && isspace(ch));

#ifdef DEBUG
  std::cerr << "ch = " << ch << std::endl;
#endif

  switch (ch)
    {
    case ';':
      {
	return curr_tok = PRINT;
      }
    case '\n':
      {
	line++;
	return curr_tok = PRINT;
      }
    case '*':
    case '/':
    case '+':
    case '-':
    case '(':
    case ')':
    case '=':
      {
	return curr_tok = Token_value(ch);
      }
    case '0':
    case '1':
    case '2':
    case '3':
    case '4':
    case '5':
    case '6':
    case '7':
    case '8':
    case '9':
    case '.':
      {
	input->putback(ch);
	*input >> number_value;

#ifdef DEBUG
	std::cerr << "got number " << number_value << std::endl;
	std::cerr << "return curr_tok = NUMBER" << std::endl;
#endif

	return curr_tok = NUMBER;
      }
    default:
      if (isalpha(ch))
	{
	  string_value = ch;
	  while (input->get(ch) && isalnum(ch))
	    {
	      string_value.push_back(ch);
	    }

#ifdef DEBUG
	  std::cerr << "string_value = " << string_value << std::endl;
#endif

	  input->putback(ch);
	  return curr_tok = NAME;
	}
      error("bad token");
      return curr_tok = PRINT;
    }
}

int 
main(int argc, char* argv[])
{
  line = 1;
  table["pi"] = 3.14;
  table["e"] = 2.72;

  switch (argc)
    {
    case 1:
      {
	input = &std::cin;
	break;
      }
    case 2:
      {
	input = new std::istringstream(argv[1]);
	break;
      }
    default:
      error("too many arguments!");
      return 1;
    }

  while (*input)
    {
      get_token();

#ifdef DEBUG
      output_curr_tok();
#endif

      if (curr_tok == END)
	{
	  break;
	}

      if (curr_tok == PRINT)
	{
	  continue;
	}

      std::cout << expr(false) << std::endl;
    }

  if (input != &std::cin)
    {
      delete input;
    }
  return no_of_errors;
}
